/*!
 * @file CollisionSoundActuator.cpp
 * @author Rocco Martino
 */
/***************************************************************************
 *   Copyright (C) 2014 by Rocco Martino                                   *
 *   martinorocco@gmail.com                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License as        *
 *   published by the Free Software Foundation; either version 2.1 of the  *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this program; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

/* ======================================================================= */
/* ....................................................................... */
#include <ooGame/CollisionSoundActuator>

#include <osgODE/Notify>
#include <osgODE/RigidBody>
#include <osgODE/World>

#if OOGAME_USE_PSOUND
    #include <pSound/Source>
#else
    namespace pSound {
        class Source: public osg::Referenced
        {
        } ;
    }
#endif
/* ....................................................................... */
/* ======================================================================= */




using namespace ooGame ;




/* ======================================================================= */
/* ....................................................................... */
CollisionSoundActuator::CollisionSoundActuator(void):
    m_mode              ( PLAY_STOP ),
    m_sound3d           ( true ),
    m_gain              ( 1.0 ),
    m_pitch             ( 1.0 ),
    m_waiting_false     ( false ),
    m_gain_multiplier   ( 1.0 ),
    m_dirty_gain_levels ( false ),
    m_use_gain_levels   ( false )
{
}
/* ....................................................................... */
/* ======================================================================= */




/* ======================================================================= */
/* ....................................................................... */
CollisionSoundActuator::CollisionSoundActuator(const CollisionSoundActuator& other, const osg::CopyOp& copyop):
    ooGame::Actuator    ( other, copyop ),
    m_mode              ( other.m_mode ),
    m_sound3d           ( other.m_sound3d ),
    m_gain              ( other.m_gain ),
    m_pitch             ( other.m_pitch ),
#if OOGAME_USE_PSOUND
    m_source            ( osg::clone( other.m_source.get(), copyop ) ),
#else
    m_source            ( other.m_source.get() ),
#endif
    m_waiting_false     ( other.m_waiting_false ),
    m_gain_levels       ( other.m_gain_levels ),
    m_gain_curve        ( other.m_gain_curve ),
    m_gain_multiplier   ( other.m_gain_multiplier ),
    m_dirty_gain_levels ( other.m_dirty_gain_levels ),
    m_use_gain_levels   ( other.m_use_gain_levels )
{
}
/* ....................................................................... */
/* ======================================================================= */




/* ======================================================================= */
/* ....................................................................... */
CollisionSoundActuator::~CollisionSoundActuator(void)
{
}
/* ....................................................................... */
/* ======================================================================= */




/* ======================================================================= */
/* ....................................................................... */
void
CollisionSoundActuator::computeGainCurve(void)
{
    const ooReal    a = m_gain_levels.x() ;
    const ooReal    b = m_gain_levels.y() ;
    const ooReal    c = m_gain_levels.z() ;

    m_gain_curve.x() = -a ;
    m_gain_curve.y() = 1.0 / ( c - a ) ;
    m_gain_curve.z() = log(0.5) / log( (b-a) / (c-a) ) ;
}
/* ....................................................................... */
/* ======================================================================= */




/* ======================================================================= */
/* ....................................................................... */
void
CollisionSoundActuator::operator()( osgODE::ODEObject* object, Game* game )
{
#if OOGAME_USE_PSOUND
    osgODE::RigidBody*  body = object->asRigidBody() ;


    PS_ASSERT1( m_source.valid() ) ;
    PS_ASSERT1( body ) ;
    PS_ASSERT1( body->getWorld() ) ;
    PS_ASSERT1( body->getWorld()->getFrontEventsBuffer() ) ;

    (void) game ;


    if( ! m_source.valid() ) {
        return ;
    }

    const osgODE::Events::EventList&    events = body->getWorld()->getFrontEventsBuffer()->getEventList() ;


    if( events.size() > 0 ) {

        const osg::Matrix&  view_matrix = events[0].second->getCamera()->getViewMatrix() ;


        if( m_sound3d ) {
            m_source->setPosition( body->getPosition() * view_matrix ) ;
            m_source->setDirection( (body->getQuaternion() * osg::Z_AXIS) * view_matrix ) ;
        } else {
            m_source->setPosition( osg::Vec3() ) ;
            m_source->setDirection( osg::Z_AXIS ) ;
        }
    }



    m_source->setParam( pSound::Source::POSITION, m_source->getPosition() ) ;
    m_source->setParam( pSound::Source::DIRECTION, m_source->getDirection() ) ;




    if( m_dirty_gain_levels && m_use_gain_levels ) {
        computeGainCurve() ;

        m_dirty_gain_levels = false ;
    }




    if( m_source->isPlaying() ) {
        ooReal  gain = m_source->getParam( pSound::Source::GAIN ) ;


        gain = gain + (1.0 - gain) * 0.1 ;
        m_source->setParam( pSound::Source::GAIN, gain * m_gain_multiplier * m_gain ) ;
    }


    if( isActive() ) {

        switch( m_mode )
        {
            case PLAY_STOP:
                if( ! m_waiting_false ) {

                    if( m_use_gain_levels ) {
                        const ooReal    V = body->getLinearVelocity().length() + m_gain_curve.x() ;

                        if( V > 0.0 ) {
                            m_gain_multiplier = pow( V * m_gain_curve.y(), m_gain_curve.z() ) ;
                            m_gain_multiplier = osg::clampTo( m_gain_multiplier, (ooReal)1.0e-3, (ooReal)1.0 ) ;
                        } else {
                            m_gain_multiplier = 1.0e-3 ;
                        }

                    } else {
                        m_gain_multiplier = 1.0 ;
                    }

                    m_source->play() ;
                    m_source->setParam( pSound::Source::GAIN, 1.0e-3 ) ;
                }
            break ;

            case PLAY_END:
                if( ! (m_waiting_false || m_source->isPlaying()) ) {

                    if( m_use_gain_levels ) {
                        const ooReal    V = body->getLinearVelocity().length() + m_gain_curve.x() ;

                        if( V > 0.0 ) {
                            m_gain_multiplier = pow( V * m_gain_curve.y(), m_gain_curve.z() ) ;
                            m_gain_multiplier = osg::clampTo( m_gain_multiplier, (ooReal)1.0e-3, (ooReal)1.0 ) ;
                        } else {
                            m_gain_multiplier = 1.0e-3 ;
                        }

                    } else {
                        m_gain_multiplier = 1.0 ;
                    }

                    m_source->play() ;
                    m_source->setParam( pSound::Source::GAIN, 1.0e-3 ) ;
                }
            break ;


            case LOOP_STOP:
            case LOOP_END:
                if( ! m_source->isPlaying() ) {
                    m_source->play() ;
                }
            break ;


            default:
                PS_BREAKPOINT() ;
            break ;
        }


        m_waiting_false = true ;



    } else { // isActive()

        switch( m_mode )
        {
            case PLAY_END:
            case LOOP_END:
                // nothing
            break ;


            case PLAY_STOP:
            case LOOP_STOP:
                if( m_source->isPlaying() ) {
                    m_source->stop() ;
                }
            break ;


            default:
                PS_BREAKPOINT() ;
            break ;
        }



        m_waiting_false = false ;

    }


#else /* OOGAME_USE_PSOUND */

    (void) object ;
    (void) game ;
#endif
}
/* ....................................................................... */
/* ======================================================================= */




/* ======================================================================= */
/* ....................................................................... */
void
CollisionSoundActuator::setSource( pSound::Source* source )
{
    m_source = source ;
}
/* ....................................................................... */
/* ======================================================================= */
